#include <iostream>
#include <typeinfo>
#include <cassert>
#include <string>
#include <any>

class Any//保存任意自定义类型的应用层协议，应用层协议最终还是面向对象的，像该Http服务器中的HttpReqContext类型，还有可能是其他的自定义类型
{
private:
    //设计思想：在Any类里面搞一个多态，派生类用于保存任意类型的数据，而Any用于保存基类对象的指针，父类指针正好可以指向子类对象
    class holder// (holder -> 持有人 placeholder -> 占位符)
    {
    public:
        holder() {}
        virtual ~holder() {}
        virtual const std::type_info &type() = 0; // 纯虚函数，迫使子类进行重写
        virtual holder *clone() = 0;
        // 针对一个子类对象克隆出新的对象出来，因为未来可能会进行Any对象的拷贝构造，所以我们也要能够让存储数据的对象也拷贝出来一份
    };
    template <class T>
    class placeholder : public holder
    {
    public:
        placeholder(const T &val) // 单参数构造
            : _val(val)
        {}
        virtual const std::type_info &type()
        {
            return typeid(T);
        }
        virtual holder *clone() // 针对当前的对象自身，克隆出一个新的子类对象，让新Any对象中的父类指针指向新的子类对象
        {
            return new placeholder<T>(_val); // 新new出来一个子类对象，然后返回对象地址，只不过返回的类型是父类指针类型
        }
        virtual ~placeholder()
        {
            // 默认析构，对于内置类型不处理，对于自定义类型，调用他们类内的析构函数
            // 能执行析构函数进行资源的清理，则先执行析构函数。然后就调用operator delete完成对象所占用空间的释放销毁
        }
    public:
        T _val; // 保存任意类型的数据
    };
private:
    holder *_data_ptr;
    /*holder不是模板类，所以holder定义指针变量_data_ptr时，无须带有特定的类型，因为人家不是模板类！
    而需要是模板类来负责保存任意类型数据对象的是placeholder这个类，这样就可以完成Any的最主要的两个功能*/
    // 1.保存任意类型的数据(placeholder模板类来负责保存) 2.构造Any对象时，无须带有特定的类型(->Any的成员变量不带有模板参数)
public:
    Any() : _data_ptr(NULL) {}
    //模板函数，参数是任意类型，可以接收任意类型的数据对象，
    template <class T>
    Any(const T &val)
        : _data_ptr(new placeholder<T>(val))
    {}
    Any(const Any &other)
        : _data_ptr(other._data_ptr ? other._data_ptr->clone() : NULL)
    // 如果other中的父类指针不为空，则克隆一份新的数据对象出来，并将对象的地址拷贝给当前Any类对象保存的父类指针中，
    // 如果other中的父类指针为空，则也将当前Any对象中保存的父类指针置为NULL
    {}
    ~Any()
    {
        delete _data_ptr; //调用_data_ptr指向的placeholder对象内的析构函数，清理资源后调用free，释放_data_ptr变量所占用的空间
        _data_ptr = NULL;
    }
    template <class T>
    T *get() // 返回Any保存的数据对象的指针
    {
        // 用户想要获取的数据类型和Any中保存的数据对象的类型必须一致
        assert(typeid(T) == _data_ptr->type());
        return &((placeholder<T> *)_data_ptr)->_val;
    }
    Any &swap(Any &other)
    {
        std::swap(_data_ptr, other._data_ptr);
        return *this;
    }
    template <class T>
    Any &operator=(const T &val)
    {
        Any(val).swap(*this);//将构造出来的临时Any对象与当前的Any对象进行交换，则临时对象销毁时，原来的Any对象所保存的数据也就释放了
        return *this;
    }
    Any &operator=(const Any &other)
    {
        Any(other).swap(*this);
        return *this;
    }
};
class Test
{
private:
    int _a;
public:
    Test() { std::cout << "构造函数"  << std::endl; }
    Test(const Test& t) { std::cout << "拷贝构造" << std::endl; }
    ~Test() { std::cout << "析构函数" << std::endl; }
};
int main()
{
    std::any a3(Test());


    /* Any a(10);
    int *pi = a.get<int>(); // 获取any容器中存储的内容对象的指针
    std::cout << *pi << std::endl;

    a = std::string("rygttm");
    std::string *ps = a.get<std::string>(); // 获取any容器中存储的内容对象的指针
    std::cout << *ps << std::endl;
    *ps = "hello world"; // 通过指针来修改any中存储的值
    std::cout << *ps << std::endl;*/

    //---测试Any容器是否有内存泄露
    // Test();
    // Any a2(Test());

    //c++17: 使用std中的any

    std::any a2(100);
    //any_cast使用方式1: 使用指针来访问
    int *pi = std::any_cast<int>(&a2);
    std::cout << *pi << std::endl;
    //any_cast使用方式2: 直接返回存储数据对象的引用
    std::cout << std::any_cast<int>(a2) << std::endl; 

    std::any a1(std::string("hello world"));
    std::cout << std::any_cast<std::string>(a1) << std::endl; 
 
    return 0;
}